// Copyright (C) 2012 Yury Bychenkov (http://bychenkov.com).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//  - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//  - Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//  - Neither the name of Yury Bychenkov nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

inline uint IntegerTraits::sizeOf(const uint8* p) {
	uint8 v = p[0] & 0x0FU;
	return v > 8? 1 : (v + 1);
}

inline uint IntegerTraits::safeSizeOf(const uint8* p, uint available) {
	uint8 v = p[0] & 0x0FU;
	return v > 8? 1 : (v + 1);
}

inline int64 IntegerTraits::decode(const uint8* p) {
	const uint8 size = (p[0] & 0x0FU) + 1;
	if (size > 8) {
		return size == 9? *(int64*) (p + 1) : NULL_VALUE;
	}
	const uint shift = 64 - (size << 3);
	return ((*(int64*) p) << shift) >> (shift + 4); // unsafe
}

inline uint IntegerTraits::encodeNull(uint8* p) {
	p[0] = 0xFF;
	return 1;
}

inline uint IntegerTraits::encode(int64 element, uint8* p) {
	uint size = 8;
	int64 m = 0xF800000000000000ULL;
	int64 v = element < 0?  ~element : element;
	while (size > 0 && (v & m) == 0) {
		size--;
		m >>= 8;
	}
	if (size == 8) {
		if (element == NULL_VALUE) {
			p[0] = 0xFF;
			return 1;
		}
		p[0] = 8;
		*(int64*) (p + 1) = element;
		return 9;
	}

	*(int64*) p = (element << 4) | size;

	return size + 1;
}

inline int IntegerTraits::compare(const uint8* element1, const uint8* element2) {
	int64 v1 = decode(element1);
	int64 v2 = decode(element2);
	return v1 < v2? -1 : (v1 > v2? 1 : 0);
}



inline int StringTraits::encode(const uint8* element, uint elementSize, uint8* data, uint dataSize) {
	// TODO hashcode
	if (element == 0) {
		if (dataSize > 0) {
			data[0] = 0xFF;
			return 1;
		}
		return -1;
	}

	uint size = elementSize;
	if (elementSize < 0xFE) {
		size++;
		if (size > dataSize) {
			return -int(size);
		}
		*data++ = (uint8) elementSize;
	} else if (elementSize < 0x1000000) {
		size += 4;
		if (size > dataSize) {
			return -int(size);
		}
		*((uint32*) data) = (elementSize << 8) | 0xFE;
		data += 4;
	} else {
		return 0;
	}

	for (uint i = 0; i < elementSize; i++) {
		data[i] = element[i];
	}

	return size;
}

inline uint StringTraits::encodeNull(uint8* data) {
	data[0] = 0xFF;
	return 1;
}

inline const uint8* StringTraits::decode(const uint8* element, uint& size) {
	uint8 v = element[0];
	if (v < 0xFE) {
		size = v;
		return element + 1;
	} else if (v == 0xFF) {
		size = 0;
		return 0;
	}

	size = ((*((uint32*) element)) >> 8);
	return element + 4;
}


inline uint StringTraits::sizeOf(const uint8* element) {
	uint8 v = element[0];
	if (v < 0xFE) {
		return v + 1;
	} else if (v == 0xFE) {
		return ((*((uint32*) element)) >> 8) + 4;
	}
	return 1;
}

inline uint StringTraits::safeSizeOf(const uint8* element, uint available) {
	uint8 v = element[0];
	if (v < 0xFE) {
		return v + 1;
	} else if (v == 0xFF) {
		return 1;
	}

	if (available < 4) {
		return 0;
	}
	
	uint size = ((*((uint32*) element)) >> 8) + 4;

	return size <= available? size : 0;
}

inline bool StringTraits::equal(const uint8* element1, const uint8* element2) {
	uint s1 = element1[0];
	uint s2 = element2[0];
	if (s1 != s2) {
		return false;
	} else if (s1 == 0xFF) {
		return true;
	}

	if (s1 == 0xFE) {
		s1 = (*((uint32*) element1)) >> 8;
		s2 = (*((uint32*) element1)) >> 8;
		if (s1 != s2) {
			return false;
		}
		element1 += 4;
		element2 += 4;
	} else {
		element1++;
		element2++;
	}

	for (uint i = 0; i < s1; i++) {
		if (element1[i] != element2[i]) {
			return false;
		}
	}

	return true;

}

inline int StringTraits::compare(const uint8* element1, const uint8* element2) {
	uint s1 = element1[0];
	if (s1 == 0xFF) {
		return element2[0] == 0xFF? 0 : -1;
	}

	uint s2 = element2[0];
	if (s2 == 0xFF) {
		return 1;
	}

	if (s1 < 0xFE) {
		element1++;
	} else {
		s1 = (*((uint32*) element1)) >> 8;
		element1 += 4;
	}

	if (s2 < 0xFE) {
		element2++;
	} else {
		s2 = (*((uint32*) element2)) >> 8;
		element2 += 4;
	}

	const uint s = min(s1, s2);
	for (uint i = 0; i < s; i++) {
		if (element1[i] != element2[i]) {
			return element1[i] < element2[i]? -1 : 1;
		}
	}

	return (int) s1  - (int) s2;
}

inline int DecimalTraits::encode(const uint8* element, uint elementSize, uint8* data, uint dataSize) {
	if (element == 0) {
		if (dataSize > 0) {
			data[0] = 0xFF;
			return 1;
		}
		return -1;
	}

	if (elementSize > MAX_ELEMENT_SIZE) {
		return 0;
	}


	uint size = elementSize + 1;
	if (size > dataSize) {
		return -int(size);
	}
	*data++ = (uint8) elementSize;

	for (uint i = 0; i < elementSize; i++) {
		data[i] = element[i];
	}

	return size;
}

inline uint DecimalTraits::encodeNull(uint8* data) {
	data[0] = 0xFF;
	return 1;
}

inline const uint8* DecimalTraits::decode(const uint8* element, uint& size) {
	if (element[0] == 0xFF) {
		size = 0;
		return 0;
	}
	size = element[0];
	return element + 1;
}

inline uint DecimalTraits::sizeOf(const uint8* element) {
	uint8 v = element[0];
	return v == 0xFF? 1 : (v + 1);
}

inline uint DecimalTraits::safeSizeOf(const uint8* element, uint available) {
	uint8 v = element[0];
	if (v == 0xFF) {
		return 1;
	}
	return v == 0xFF? 1 : (v < available? v + 1 : 0);
}

inline bool DecimalTraits::equal(const uint8* element1, const uint8* element2) {
	uint s1 = element1[0];
	uint s2 = element2[0];
	if (s1 != s2) {
		return false;
	} else if (s1 == 0xFF) {
		return true;
	}

	for (uint i = 0; i < s1; i++) {
		if (element1[i] != element2[i]) {
			return false;
		}
	}

	return true;

}

inline int DecimalTraits::compare(const uint8* element1, const uint8* element2) {
	uint s1 = element1[0];
	if (s1 == 0xFF) {
		return element2[0] == 0xFF? 0 : -1;
	}

	uint s2 = element2[0];
	if (s2 == 0xFF) {
		return 1;
	}
	const uint s = min(s1, s2);
	for (uint i = 0; i < s; i++) {
		if (element1[i] != element2[i]) {
			return element1[i] < element2[i]? -1 : 1;
		}
	}

	return (int) s1  - (int) s2;
}



inline uint TupleTraits::sizeOf(const uint8* element) {
	uint8 v = element[0];
	if (v < 0xFF) {
		return v + 1;
	} else {
		return ((*((uint32*) element)) >> 8) + 4;
	}
}

inline uint TupleTraits::safeSizeOf(const uint8* element, uint available) {
	uint8 v = element[0];
	if (v < 0xFF) {
		return v + 1;
	}

	if (available < 4) {
		return 0;
	}
	
	uint size = ((*((uint32*) element)) >> 8) + 4;

	return size <= available ? size : 0;
}

inline bool TupleTraits::equal(const uint8* element1, const uint8* element2) {
	uint s1 = element1[0];
	uint s2 = element2[0];
	if (s1 != s2) {
		return false;
	}

	if (s1 == 0xFF) {
		s1 = (*((uint32*) element1)) >> 8;
		s2 = (*((uint32*) element1)) >> 8;
		if (s1 != s2) {
			return false;
		}
		element1 += 4;
		element2 += 4;
	} else {
		element1++;
		element2++;
	}

	for (uint i = 0; i < s1; i++) {
		if (element1[i] != element2[i]) {
			return false;
		}
	}

	return true;

}

inline int TupleTraits::compare(const uint8* element1, const uint8* element2) {
	uint s1 = element1[0];
	uint s2 = element2[0];

	if (s1 < 0xFF) {
		element1++;
	} else {
		s1 = (*((uint32*) element1)) >> 8;
		element1 += 4;
	}

	if (s2 < 0xFF) {
		element2++;
	} else {
		s2 = (*((uint32*) element2)) >> 8;
		element2 += 4;
	}

	if (s1 == s2) {
		for (uint i = 0; i < s1; i++) {
			if (element1[i] != element2[i]) {
				return element1[i] < element2[i]? -1 : 1;
			}
		}
		return 0;
	} else {
		while (*element1 == *element2) {
			element1++;
			element2++;
		}
		return *element1 < *element2? -1 : 1;
	}
}





template <uint elementSize>
uint FixedSizeElementTraits<elementSize>::sizeOf(const uint8* /*element*/) {
	return elementSize;
}

template <uint elementSize>
uint FixedSizeElementTraits<elementSize>::safeSizeOf(const uint8* /*element*/, uint available) {
	return available < elementSize? 0: elementSize;
}

template <uint elementSize>
int FixedSizeElementTraits<elementSize>::compare(const uint8* element1, const uint8* element2) {
	for (int i = elementSize - 1; i >= 0 ; i--) {
		if (element1[i] != element2[i]) {
			return element1[i] < element2[i]? -1: 1;
		}
	}
	return 0;
}
